package com.heima.search.service.Impl;

import com.alibaba.fastjson.JSON;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dto.SearchDto;
import com.heima.model.search.enums.AppArticleInfo;
import com.heima.search.dto.ApAssociateSearchDto;
import com.heima.search.dto.HistorySearchDto;
import com.heima.search.pojo.ApAssociateWords;
import com.heima.search.pojo.ApUserSearch;
import com.heima.search.service.SearchService;
import com.heima.utils.common.WmThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SearchImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private MongoTemplate mongoTemplate;


    //es根据key查询数据
    @Override
    public ResponseResult searchByKey(SearchDto searchDto) throws IOException {
        if (searchDto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        Long userId = WmThreadLocalUtil.get();
        this.addSearchDataMongodb(searchDto.getSearchWords(), Integer.parseInt(userId + ""));

        //连接索引
        SearchRequest request = new SearchRequest("app_article_info");
        String searchWords = searchDto.getSearchWords();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        if (!StringUtils.isEmpty(searchWords)) {
            boolQueryBuilder.must(QueryBuilders.multiMatchQuery(searchWords, "title", "content"));
        }
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(searchDto.getMinBehotTime()));
        request.source().query(boolQueryBuilder);  //添加条件对象
        request.source().sort("publishTime", SortOrder.DESC);//排序
        HighlightBuilder highlight = new HighlightBuilder();
        highlight.field("title");
        highlight.preTags("<font style='color:red; font-size: inherit;'>");
        highlight.postTags("</font>");
        request.source().highlighter(highlight);//高亮
        request.source().size(10);//分页
        SearchResponse search = client.search(request, RequestOptions.DEFAULT);//发送请求
        List<Map> appArticleInfos = getAppArticleInfos(search);
        return ResponseResult.okResult(appArticleInfos);
    }


    //解析条件查询结果
    private List<Map> getAppArticleInfos(SearchResponse search) {
        SearchHit[] hits = search.getHits().getHits();//拿到查询后的结果集合
        List<Map> collect = Arrays.stream(hits).map(hit -> {
            String sourceAsString = hit.getSourceAsString();//拿到具体的数据部分
            //转换为对象
            AppArticleInfo appArticleInfo = JSON.parseObject(sourceAsString, AppArticleInfo.class);
            //获取高亮部分集合,解析数据封装到对象里
            Map map = new HashMap();
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                map = JSON.parseObject(sourceAsString, Map.class);
                HighlightField title = highlightFields.get("title");
                if (title != null) {
                    Text[] fragments = title.getFragments();
                    String join = StringUtils.join(fragments, ",");
                    map.put("h_title", join);
                    return map;
                } else {
                    map.put("h_title", appArticleInfo.getTitle());
                    return map;
                }
            }
            return map;
        }).collect(Collectors.toList());
        return collect;
    }


    //监听文章上传  数据同步
    @Override
    public void addApplication(AppArticleInfo appArticleInfo) {
        try {
            IndexRequest request = new IndexRequest("app_article_info").id(appArticleInfo.getId() + "");
            String json = JSON.toJSONString(appArticleInfo);
            request.source(json, XContentType.JSON);
            client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException("监听文章添加es,数据同步异常");
        }
    }


    //历史搜索记录放入mongodb中
    @Override
    public void addSearchDataMongodb(String searchWords, Integer userId) {
        if (searchWords == null) {
            return;
        }
        log.info("当前搜索线程名字为:{}" + Thread.currentThread().getName());
        //判断用户关键字历史记录是否存在决定是新增还是修改
        Query query = Query.query(Criteria.where("userId").is(userId).and("keyword").is(searchWords));
        ApUserSearch apUserSearche = mongoTemplate.findOne(query, ApUserSearch.class);
        if (apUserSearche != null) {
            //1.已存在 修改
            apUserSearche.setCreatedTime(new Date());
            mongoTemplate.save(apUserSearche);
            return;
        }

        //不承存在,如果长度已经到达了10位数(业务需求历史数据长度只保留10位)
        Query query1 = Query.query(Criteria.where("userId").is(userId))
                .with(Sort.by(Sort.Direction.DESC, "createdTime"));
        List<ApUserSearch> apUserSearches = mongoTemplate.find(query1, ApUserSearch.class);
        //长度大于10
        if (apUserSearches.size() >= 10) {
            ApUserSearch apUserSearch = apUserSearches.get(apUserSearches.size() - 1);
            apUserSearch.setCreatedTime(new Date());
            apUserSearch.setKeyword(searchWords);
            mongoTemplate.save(apUserSearch);
        } else {
            //长度小于10
            ApUserSearch apUserSearch = new ApUserSearch();
            apUserSearch.setKeyword(searchWords);
            apUserSearch.setUserId(userId);
            apUserSearch.setCreatedTime(new Date());
            mongoTemplate.save(apUserSearch);
        }
    }

    //查询搜索记录列表
    @Override
    public ResponseResult querySearchData() {
        Query query = Query.query(Criteria.where("userId").is(WmThreadLocalUtil.get()));
        List<ApUserSearch> apUserSearches = mongoTemplate.find(query, ApUserSearch.class);
        return ResponseResult.okResult(apUserSearches);
    }

    //删除搜索历史记录
    @Override
    public ResponseResult deleteSearch(HistorySearchDto historySearchDto) {
        if (StringUtils.isEmpty(historySearchDto.getId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        Query query = Query.query(Criteria.where("id").is(historySearchDto.getId()));
        mongoTemplate.remove(query, ApUserSearch.class);
        return ResponseResult.okResult("删除成功");
    }

    //联想词搜索
    @Override
    public ResponseResult queryAssociate(ApAssociateSearchDto dto) {
        if (StringUtils.isEmpty(dto.getSearchWords())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getPageSize() > 10) {
            dto.setPageSize(10);
        }


        Query query = Query.query(Criteria.where("associateWords").regex(".*?" + dto.getSearchWords() + ".*"))
                .limit(dto.getPageSize());
        List<ApAssociateWords> apAssociateWords = mongoTemplate.find(query, ApAssociateWords.class);
        return ResponseResult.okResult(apAssociateWords);
    }
}
